#include "ipc.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <semaphore.h>

#define	SHRMEM 	"SharedMemory"
#define	SIZE 	51200
#define MAX_CLIENTS 20

#define	SERVER_PUSH 	"s_w"
#define	SERVER_POP 	"s_r"
#define	VACIO_POP 	"v_r"
#define	VACIO_PUSH 	"v_w"

#define CLIENT_POP	"c_r%d"
#define CLIENT_PUSH	"c_w%d"

#define MUTEX_PID "no_me_piso_mas"

#define MAX_TEXT 512
#define MAX_MESSAGES (SIZE-sizeof(pids)*MAX_CLIENTS)/sizeof(msg_t)

typedef struct{
	int pid;
	int size;
	char text[MAX_TEXT];
	int next;
} msg_t;

typedef struct{
	int first;
	int last;
	int pid;
} pids;

typedef struct{
	pids pids[MAX_CLIENTS + 2];
	msg_t messages[MAX_MESSAGES];
} shm_t;

int cant=2;
int my_pos=-1;
shm_t * mem;
int firstTime=1;
	
sem_t* server_push;
sem_t* server_pop;
sem_t* vacio_pop;
sem_t* vacio_push;
sem_t* client_pop;
sem_t* client_push;

sem_t* mutex_pid;
	
int ipc_open(char* filepath, char* argv[]){

	if(firstTime){
		int fd;
	
		shm_unlink(SHRMEM);
		if ( (fd = shm_open(SHRMEM, O_RDWR|O_CREAT, 0666)) == -1 ){
			printf("SHRMEM_ERROR\n");
			exit(-21);
		}
		ftruncate(fd, SIZE);
	
		if ( !(mem = (shm_t *) mmap(NULL, SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) ){
			printf("SHRMEM_ERROR\n");
			exit(-21);
		}

		sem_unlink(MUTEX_PID);
		mutex_pid=sem_open(MUTEX_PID , O_CREAT | 0666 , 0777, 1);
		add_semephore(MUTEX_PID, mutex_pid);
	
		sem_unlink(SERVER_PUSH);
		server_push= sem_open(SERVER_PUSH, O_CREAT | 0666 , 0777, 1);
		add_semephore(SERVER_PUSH, server_push);
	
		sem_unlink(SERVER_POP);
		server_pop= sem_open(SERVER_POP, O_CREAT | 0666 , 0777, 0);
		add_semephore(SERVER_POP, server_pop);
	
		sem_unlink(VACIO_POP);
		vacio_pop= sem_open(VACIO_POP, O_CREAT | 0666 , 0777, 1);
		add_semephore(VACIO_POP, vacio_pop);
	
		sem_unlink(VACIO_PUSH);
		vacio_push= sem_open(VACIO_PUSH, O_CREAT | 0666 , 0777, 1);
		add_semephore(VACIO_PUSH, vacio_push);
		
		close(fd);
	
		mem->pids[0].first=0;
		mem->pids[0].last=MAX_MESSAGES-1;
		int i;
		for(i=1; i < MAX_CLIENTS + 2; i++){
			mem->pids[i].first=-1;
			mem->pids[i].last=-1;
		}
	
		for(i=0; i< MAX_MESSAGES -1; i++){
			mem->messages[i].next=i+1;
		}
		mem->messages[MAX_MESSAGES-1].next=-1;

		firstTime=0;
	}
			
	int ret;
	switch(ret=fork()){
		case -1:
			printf("error2");
			exit(-21);
		case 0:

			execve(filepath,argv,NULL);
		default:
		
			return ret;
	}
	
	
  
}

int ipc_msg_recive(int from, message_t* message){


	int flag=0;
	int pos;
	if(from == SERVER){
		sem_wait(client_pop);
		pos=mem->pids[my_pos].first;
		mem->pids[my_pos].first=mem->messages[pos].next;
		
		if(mem->pids[my_pos].first!=-1){
			sem_post(client_pop);
		}
		else{
			mem->pids[my_pos].last=-1;
		}
	}
	else{
		sem_wait(server_pop);
		pos=mem->pids[1].first;
		mem->pids[1].first=mem->messages[pos].next;
		
		if(mem->pids[1].first!=-1){
			sem_post(server_pop);
		}
		else{
			mem->pids[1].last=-1;
		}
	}
	
	
	int pid=mem->messages[pos].pid;
	message->size= mem->messages[pos].size;
	message->text=malloc(message->size);//FREE
	memcpy(message->text,mem->messages[pos].text,message->size);
	mem->messages[pos].next=-1;
	
	
	sem_wait(vacio_push);
	
	if(mem->pids[0].last == -1){
		mem->pids[0].first=pos;
		flag=1;
	}
	else{
		mem->messages[mem->pids[0].last].next=pos;
	}
	mem->pids[0].last=pos;
	
	sem_post(vacio_push);
	if(flag){
		sem_post(vacio_pop);
	}

	return pid;
	
	
}

int ipc_msg_send(int pid, message_t* message){
	
	
	if( (pid == SERVER) && firstTime){		
		server_push= sem_open(SERVER_PUSH, 0);
		server_pop= sem_open(SERVER_POP, 0);
		vacio_push= sem_open(VACIO_PUSH, 0);
		vacio_pop= sem_open(VACIO_POP, 0);
	
		char name[10];
		
		sprintf(name,CLIENT_POP,getpid());
		sem_unlink(name);
		client_pop= sem_open(name, O_CREAT | 0666 , 0777, 0);
		add_semephore(name, client_pop);
		
		sprintf(name,CLIENT_PUSH,getpid());
		sem_unlink(name);
		client_push= sem_open(name, O_CREAT | 0666 , 0777, 1);
		add_semephore(name, client_push);
		
		int fd;
		
		if ( (fd = shm_open(SHRMEM, O_RDWR, 0666)) == -1 ){
			printf("SHRMEM_ERROR\n");
			exit(-21);
		}
		ftruncate(fd, SIZE);
		
		if ( !(mem = (shm_t *) mmap(NULL, SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) ){
			printf("SHRMEM_ERROR\n");
			exit(-21);
		}

		sem_t* mutex_pid= sem_open(MUTEX_PID, 0);
		sem_wait(mutex_pid);
		int i;
		for(i=2;i<MAX_CLIENTS + 2 && my_pos==-1 ;i++){
			if(mem->pids[i].pid==0){
				mem->pids[i].pid=getpid();
				my_pos=i;
			}
		}
		sem_post(mutex_pid);
		
		firstTime=0;
	}
	
	int flag=0;

	sem_wait(vacio_pop);
	int pos=mem->pids[0].first;
	mem->pids[0].first=mem->messages[pos].next;
	
	if(mem->pids[0].first!=-1){
		sem_post(vacio_pop);
	}
	else{
		mem->pids[0].last=-1;
	}
	
	mem->messages[pos].pid= pid==SERVER? my_pos : SERVER;
	mem->messages[pos].size=message->size;
	memcpy(mem->messages[pos].text,message->text,message->size);
	mem->messages[pos].next=-1;
	
	if(pid==SERVER){
		sem_wait(server_push);
		if(mem->pids[1].last == -1){
			mem->pids[1].first=pos;
			flag=1;
		}
		else{
			mem->messages[mem->pids[1].last].next=pos;
		}
		mem->pids[1].last=pos;
		
		sem_post(server_push);
		if(flag){
			sem_post(server_pop);
		}
	}
	else{
		char name[10];
		
		sprintf(name,CLIENT_PUSH,mem->pids[pid].pid);
		client_push= sem_open(name, 0);
		
		
		sem_wait(client_push);
		
		if(mem->pids[pid].last == -1){
			mem->pids[pid].first=pos;
			flag=1;
		}
		else{
			mem->messages[mem->pids[pid].last].next=pos;
		}
		mem->pids[pid].last=pos;
		
		sem_post(client_push);
		
		if(flag){
			sprintf(name,CLIENT_POP,mem->pids[pid].pid);
			client_pop= sem_open(name, 0);

			
			sem_post(client_pop);
			
			sem_close(client_pop);
		}
		
		sem_close(client_push);
	}
	

	return 1;

}

void ipc_close(){
	
	shm_unlink(SHRMEM );
	munmap(mem, SIZE);
	
}
